package com.jyz.spring;

import com.jyz.spring.annotation.*;
import org.apache.commons.lang.StringUtils;

import java.beans.Introspector;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class JyzApplicationContext {


    private Map<String,BeanDefinition> beanDefinitionMap = new HashMap<>();

    // 单例池
    private Map<String,Object> singletonMap = new HashMap<>();

    public JyzApplicationContext(Class clazz){
        // 判断该类上是否有组件扫描组件
        if (clazz.isAnnotationPresent(ComponentScan.class)) {
            // 提取该组件中配置的值
            ComponentScan componentScan = (ComponentScan) clazz.getAnnotation(ComponentScan.class);
            String value = componentScan.value();
            try {
                if (StringUtils.isEmpty(value)) {
                    // 如果值为空 取当前文件的路径
                    value = getPathByLoadClass(clazz);
                    value = value.substring(0,value.lastIndexOf("."));
                }
                String path = value.replace(".", "/");
                loadFileByComponentVal(path);

            } catch (ClassNotFoundException | UnsupportedEncodingException e) {
                throw new RuntimeException(e);
            }
        }

    }

    public Object getBean(String name){
        BeanDefinition beanDefinition = beanDefinitionMap.get(name);
        if (null == beanDefinition) {
            throw new NullPointerException("不存在一个名为"+name+"的bean");
        }

        // 判断bean是否为单例bean
        if("singleton".equals(beanDefinition.getScope())){
            if(!singletonMap.containsKey(name)){
                Object bean = createBean(name, beanDefinition);
                singletonMap.put(name,bean);
            }
            return singletonMap.get(name);
        }

        return createBean(name,beanDefinition);
    }


    /**
     * 使用类加载器加载得到组件配置组件的位置
     * @Param clazz
     */
    public String getPathByLoadClass(Class clazz) throws ClassNotFoundException{
        Class<?> loadClass = this.getClass().getClassLoader().loadClass(clazz.getName());
        return loadClass.getName();
    }

    /**
     *  得到扫描路径后 遍历查找生成Bean
     * @Param val
     */
    public void loadFileByComponentVal(String val) throws UnsupportedEncodingException, ClassNotFoundException {
        ClassLoader classLoader = this.getClass().getClassLoader();
        URL resource = classLoader.getResource(val);
        String path = resource.getPath();
        String decode = URLDecoder.decode(path, "UTF-8");
        List<File> list = new ArrayList<>();
        File file = new File(decode);
        ergodicFile(file,list);
        scanFile(list);

    }

    public void ergodicFile(File file,List<File> list){
        if (file.isDirectory()) {
            for (File listFile : file.listFiles()) {
                if (listFile.isDirectory()) {
                    ergodicFile(listFile,list);
                }else{
                    list.add(listFile);
                }
            }
        }else{
            list.add(file);
        }

    }


    public void scanFile(List<File> list) throws ClassNotFoundException {
        for (File file : list) {
            String absolutePath = file.getAbsolutePath();
            String classes = absolutePath.substring(absolutePath.indexOf("classes")+"classes".length()+1, absolutePath.lastIndexOf(".class"))
                    .replace("\\",".");
            System.out.println(classes);
            ClassLoader classLoader = this.getClass().getClassLoader();
            Class<?> aClass = classLoader.loadClass(classes);
            if (aClass.isAnnotationPresent(Component.class)) {
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setType(aClass);
                beanDefinition.setLazy(aClass.isAnnotationPresent(Lazy.class));

                if (aClass.isAnnotationPresent(Scope.class)) {
                    Scope annotation = aClass.getAnnotation(Scope.class);
                    beanDefinition.setScope(annotation.value());
                }

                if (aClass.isAnnotationPresent(Lazy.class)) {
                    Lazy annotation = aClass.getAnnotation(Lazy.class);
                    beanDefinition.setLazy(annotation.value());
                }

                Component annotation = aClass.getAnnotation(Component.class);
                String value = annotation.value();
                if (StringUtils.isEmpty(value)) {
                    value = Introspector.decapitalize(aClass.getSimpleName());
                }
                if (!beanDefinitionMap.containsKey(value)) {
                    beanDefinitionMap.put(value,beanDefinition);
                    if("singleton".equals(beanDefinition.getScope()) && !beanDefinition.isLazy()){
                        // 创建Bean
                        Object bean = createBean(value, beanDefinition);

                        singletonMap.put(value,bean);
                    }

                }
            }
        }
    }

    public Object createBean(String beanName,BeanDefinition beanDefinition){
        Class aClass = beanDefinition.getType();

        try {
            Object o = aClass.newInstance();
            // 通过反射拿到所有的字段 验证字段上是否存在@AutoWired
            Field[] fields = aClass.getDeclaredFields();

            for (Field field : fields) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    String name = field.getName();
                    Object bean = getBean(name);
                    field.setAccessible(true);
                    field.set(o,bean);
                }
            }
            return o;
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }


    }



}
